package core.service;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;

import core.Constants;
import core.dto.BterPriceDTO;
import core.dto.MarketDTO;

@SuppressWarnings("unchecked")
public class URLService {
	private static Logger logger = LoggerFactory.getLogger(URLService.class);
	
	public String getRawMarketChart(String marketId) {
		String url = String.format(Constants.URL.chart, marketId);
		return getRemoteContent(url);
	}
	
	public String getRawBuyOrders(String marketId) {
		String url = String.format(Constants.URL.buy, marketId);
		return getRemoteContent(url);
	}
	
	public String getRawSellOrders(String marketId) {
		String url = String.format(Constants.URL.sell, marketId);
		return getRemoteContent(url);
	}
	
	public String getRawHisOrders(String marketId) {
		String url = String.format(Constants.URL.his, marketId);
		return getRemoteContent(url);
	}
	
	public MarketDTO getMarketInfo(String marketId, double btcPrice, double ltcPrice) {
		MarketDTO market = new MarketDTO();
		String url = String.format(Constants.URL.market, marketId);
		String content = getRemoteContent(url);
		Map<String, Object> map = JSON.parseObject(content, Map.class);
		map = JSON.parseObject("" + map.get("return"), Map.class);
		map = JSON.parseObject("" + map.get("markets"), Map.class);
		
		DecimalFormat format = new DecimalFormat(Constants.NUMBER_FORMAT_PATTERN);
		
		for (Map.Entry<String, Object> entry : map.entrySet()) {
			market = JSON.parseObject("" + entry.getValue(), MarketDTO.class);
			if ("BTC".equals(market.getSecondarycode())) {
				market.setCnyPrice(format.format(Double.valueOf(market.getLasttradeprice()) * btcPrice));
			} else if ("LTC".equals(market.getSecondarycode())) {
				market.setCnyPrice(format.format(Double.valueOf(market.getLasttradeprice()) * ltcPrice));
			}
			market.setLabel(market.getPrimarycode() + "/CNY");
		}
		return market;
	}
	
	public Double getBtcPrice() {
		try {
			return getBtcPriceFromBter().getLast();
		} catch (Exception e) {
			logger.error("query btc price from bter failed. try mtgox now...");
			try {
				String content = getRemoteContent(Constants.URL.mtgoxBtcTicker);
				Map<String, Object> map = JSON.parseObject(content, Map.class);
				map = JSON.parseObject("" + map.get("return"), Map.class);
				map = JSON.parseObject("" + map.get("last"), Map.class);
				return Double.valueOf("" + map.get("value"));
			} catch (Exception e1) {
				logger.error("query btc price from mtgox failed.");
				throw new RuntimeException("query btc price from mtgox failed.", e1);
			}
		}
	}
	
	public Double getLtcPrice() {
		try {
			return getLtcPriceFromBter().getLast();
		} catch (Exception e) {
			logger.error("query btc price from bter failed. return default value 200");
		}
		return 200d;
	}
	
	public BterPriceDTO getBtcPriceFromBter() {
		String content = getRemoteContent(Constants.URL.btc2cny);
		BterPriceDTO btcPrice = JSON.parseObject(content, BterPriceDTO.class);
		return btcPrice;
	}
	
	public BterPriceDTO getLtcPriceFromBter() {
		String content = getRemoteContent(Constants.URL.ltc2cny);
		BterPriceDTO btcPrice = JSON.parseObject(content, BterPriceDTO.class);
		return btcPrice;
	}
	
	public List<MarketDTO> getAllMarkets(double btcPrice, double ltcPrice) {
		List<MarketDTO> markets = new ArrayList<MarketDTO>();
		String content = getRemoteContent(Constants.URL.markets);
		Map<String, Object> map = JSON.parseObject(content, Map.class);
		map = JSON.parseObject("" + map.get("return"), Map.class);
		map = JSON.parseObject("" + map.get("markets"), Map.class);
		
		for (Map.Entry<String, Object> entry : map.entrySet()) {
			MarketDTO market = JSON.parseObject("" + entry.getValue(), MarketDTO.class);
			double base = 1;
			if ("BTC".equals(market.getSecondarycode())) {
				base = btcPrice;
			} else if ("LTC".equals(market.getSecondarycode())) {
				base = ltcPrice;
			}
			if (!"XPM".equals(market.getSecondarycode())) {
				double orgin = Double.valueOf(market.getLasttradeprice()) * base;;
				String d = "" + orgin;
				int len = 1;
				if (d.contains(".")) {
					d = d.split("\\.")[0];
					len = d.length();
				}
				String fmt = "0.";
				for (int i = 0; i < 9 - len; i++) {
					fmt += "0";
				}
				DecimalFormat format = new DecimalFormat(fmt);
				market.setCnyPrice(format.format(orgin));
				market.setLabel(market.getPrimarycode() + "/CNY");
				markets.add(market);
			}
		}
		Collections.sort(markets, new Comparator<MarketDTO>() {


			@Override
			public int compare(MarketDTO o1, MarketDTO o2) {
				char c1 = o1.getLabel().charAt(0);
				char c2 = o2.getLabel().charAt(0);
				return c1 - c2;
			}
			
		});
		return markets;
	}

	protected String getRemoteContent(String url) {
		StringBuilder sb = new StringBuilder();
		HttpURLConnection conn = null;
		InputStream in = null;
		try {
			conn = getRemoteConnectionByURL(url);
			conn.connect();
			in = conn.getInputStream();
			byte[] buffer = new byte[512];
			int len = -1;
			while ((len = in.read(buffer, 0, 512)) > 0) {
				sb.append(new String(buffer, 0, len));
			}
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		} finally {
			if (in != null) {
				try {
					in.close();
					in = null;
				} catch (IOException e) {
					// ignore errors...
				}
			}
			if (conn != null) {
				conn.disconnect();
				conn = null;
			}
		}
		return sb.toString();
	}
	
	protected HttpURLConnection getRemoteConnectionByURL(String urlString) throws IOException {
		try {
			URI uri = new URI(urlString);
			URL url = uri.toURL();
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setDoInput(true);
			conn.setConnectTimeout(30 * 1000);
			conn.setReadTimeout(180 * 1000);
			logger.info("Opening url: {}", url);
			return conn;
		} catch (URISyntaxException e) {
			throw new IOException(e.getMessage(), e);
		}
	}
}
